﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace XNAREUSABLES
{
    /// <summary>
    /// A collection of primatives
    /// Usage: Make a bundle, add stuff, render it.
    /// </summary>
    public class Primative_Bundle
    {
        private static GraphicsDevice     s_Graphics;
        private PrimitiveType             m_Type;                          // The type of this bundle
        public  PrimitiveType               Type
        {
            get { return m_Type; }
        }                          // Type accessor
        
        private string                    m_Name;                          // The name of this bundle
        public  string                      Name
        {
            get { return m_Name; }
        }                          // Name accessor
        
        private List<VertexPositionColor> m_Bundle_Vertices;               // The vertices stored in this bundle
        public  List<VertexPositionColor>   Vertices
        {
            get { return m_Bundle_Vertices; }
        }                      // Verticies accessor

        private VertexBuffer              m_VertexBuffer;                  // The vertex buffer for this bundle.
        private bool                      m_VertexBuffer_isDirty = true;   // Is the vertex buffer dirty?
        public  VertexBuffer                VertexBuffer
        {
            get 
            {
                if (m_VertexBuffer_isDirty)
                {
                    // Dispose of old buffer if one exists
                    if (m_VertexBuffer != null)
                    {
                        // Set vertex buffer to disposed state so GC will collect it when we drop all references.
                        m_VertexBuffer.Dispose();

                        // Drop reference to old buffer
                        m_VertexBuffer = null;
                    }

                    // Create new vertex buffer.
                    m_VertexBuffer = new VertexBuffer(s_Graphics, typeof(VertexPositionColor), m_Bundle_Vertices.Count, BufferUsage.None);
               
                    // Set vertices in buffer
                    m_VertexBuffer.SetData<VertexPositionColor>(m_Bundle_Vertices.ToArray());

                    m_VertexBuffer_isDirty = false;

                    return m_VertexBuffer;
                }
                else
                {
                    return m_VertexBuffer;
                }
            }
        }                  // Get the Vertex buffer for this bundle.

        /// <summary>
        /// Just give it graphics
        /// </summary>
        /// <param name="Graphics">The graphics device.</param>
        public static void INIT(GraphicsDevice Graphics)
        {
            s_Graphics = Graphics;
        }

        /// <summary>
        /// Constructor for creating a new bundle of a specified type, with a specified name.
        /// </summary>
        /// <param name="Bundle_Type">The type of primatives this bundle will store.</param>
        /// <param name="Bundle_Name">The index name of this bundle. MUST BE UNIQUE</param>
        public      Primative_Bundle(PrimitiveType Bundle_Type, String Bundle_Name)
        {
            // Associate members
            this.m_Type = Bundle_Type;
            this.m_Name = Bundle_Name;
            
            // Create a list for verticies
            m_Bundle_Vertices = new List<VertexPositionColor>();
        }

        /// <summary>
        /// Add a line primative to the bundle.
        /// </summary>
        /// <param name="Point0_Position">Position of point 0.</param>
        /// <param name="Point1_Position">Position of point 1.</param>
        /// <param name="Point0_Colour">Colour of point 0.</param>
        /// <param name="Point1_Colour">Colour of point 1.</param>
        public void Add_Line(Vector3 Point0_Position, Vector3 Point1_Position, Color Point0_Colour, Color Point1_Colour)
        {
            // Invalidate the vertex buffer
            m_VertexBuffer_isDirty = true;
            
            // First check if this is a valid action.
            if (!(m_Type == PrimitiveType.LineList || m_Type == PrimitiveType.LineStrip))
            {
                throw new Exception("Attempting to add a line to a Primative_Bundle constructed for: " + System.Enum.GetName(typeof(PrimitiveType), m_Type) + ".");
            }

            // Reaching here assumes the exception above was not thrown, meaning we can add the line.
            m_Bundle_Vertices.Add(new VertexPositionColor(Point0_Position, Point0_Colour));
            m_Bundle_Vertices.Add(new VertexPositionColor(Point1_Position, Point1_Colour));
        }

        /// <summary>
        /// Adds a triangle primative to the bundle.
        /// </summary>
        /// <param name="Point0_Position">Position of point 0.</param>
        /// <param name="Point1_Position">Position of point 1.</param>
        /// <param name="Point2_Position">Position of point 2.</param>
        /// <param name="Point0_Colour">Colour of point 0.</param>
        /// <param name="Point1_Colour">Colour of point 1.</param>
        /// <param name="Point2_Colour">Colour of point 2.</param>
        public void Add_Triangle(Vector3 Point0_Position, Vector3 Point1_Position, Vector3 Point2_Position, Color Point0_Colour, Color Point1_Colour, Color Point2_Colour)
        {
            // Invalidate the vertex buffer
            m_VertexBuffer_isDirty = true;

            // First check if this is a valid action.
            if (!(m_Type == PrimitiveType.TriangleList || m_Type == PrimitiveType.TriangleStrip))
            {
                throw new Exception("Attempting to add a triangle to a Primative_Bundle constructed for: " + System.Enum.GetName(typeof(PrimitiveType), m_Type) + ".");
            }

            // Reaching here assumes the exception above was not thrown, meaning we can add the triangle.
            m_Bundle_Vertices.Add(new VertexPositionColor(Point0_Position, Point0_Colour));
            m_Bundle_Vertices.Add(new VertexPositionColor(Point1_Position, Point1_Colour));
            m_Bundle_Vertices.Add(new VertexPositionColor(Point2_Position, Point2_Colour));
        }

        /// <summary>
        /// Call after setting up the shader, will simply draw.
        /// </summary>
        public void RENDER()
        {
            s_Graphics.SetVertexBuffer(VertexBuffer);

            switch (Type)
            {
                case PrimitiveType.LineList:
                    s_Graphics.DrawUserPrimitives<VertexPositionColor>(Type, Vertices.ToArray(), 0, Vertices.Count / 2);
                    break;
                case PrimitiveType.LineStrip:
                    s_Graphics.DrawUserPrimitives<VertexPositionColor>(Type, Vertices.ToArray(), 0, Vertices.Count / 2);
                    break;
                case PrimitiveType.TriangleList:
                    s_Graphics.DrawUserPrimitives<VertexPositionColor>(Type, Vertices.ToArray(), 0, Vertices.Count / 3);
                    break;
                case PrimitiveType.TriangleStrip:
                    s_Graphics.DrawUserPrimitives<VertexPositionColor>(Type, Vertices.ToArray(), 0, Vertices.Count / 3);
                    break;
            }
        }

        /// <summary>
        /// Call dispose before dropping all references to an object of this type.
        /// </summary>
        public void DISPOSE()
        {
            if (VertexBuffer != null)
            { 
                VertexBuffer.Dispose();
                m_VertexBuffer = null;
            }

        }
    }
}
